#include "serialcodec.h"

const uint8_t SerialHeadBytes[] = {0xef, 0xaa};
const uint8_t SerialTailBytes[] = {0xaa, 0x55};
const uint8_t SerialCheckBytes[] = {0xaa, 0x55};
SERIAL_UNIT_S UnitPackHeader = {0, SERIAL_PROC_TYPE_COMPARE, sizeof(SerialHeadBytes), NULL, SerialHeadBytes};
SERIAL_UNIT_S UnitPackLen = {1, SERIAL_PROC_TYPE_PACKLEN, 2, NULL, NULL};
SERIAL_UNIT_S UnitPackBody = {2, SERIAL_PROC_TYPE_SAVE, 0, NULL, NULL};
SERIAL_UNIT_S UnitPackCheck = {3, SERIAL_PROC_TYPE_CHECK, sizeof(SerialCheckBytes), NULL, SerialCheckBytes};
SERIAL_UNIT_S UnitPackTail = {4, SERIAL_PROC_TYPE_COMPARE, sizeof(SerialTailBytes), NULL, SerialTailBytes};

SERIAL_PROC_S SerialProc;
SERIAL_CTRL_S SerialCtrl;

void resetSerialProc(void)
{
    SerialCtrl.packlen = 0;
    SerialProc.procstate = 0;
    SerialCtrl.curunit = &UnitPackHeader;
    SerialCtrl.proccnt = 0;
    SerialCtrl.curstate = SERIAL_PROC_TYPE_COMPARE;
}

void buildSerialStruct(uint8_t *data)
{
    //init the proc chain
    UnitPackHeader.next = &UnitPackLen;
    UnitPackLen.next = &UnitPackBody;
    UnitPackBody.next = &UnitPackCheck;
    UnitPackCheck.next = &UnitPackTail;
    UnitPackTail.next = NULL;
    //reset ctrl block
    SerialProc.buf = data;
    SerialProc.packfixlen = \
        UnitPackHeader.unitlen + \
        UnitPackLen.unitlen + \
        UnitPackCheck.unitlen + \
        UnitPackTail.unitlen;
    SerialProc.checkfun = NULL;
    resetSerialProc();
}

void jumpSerialNext(void)
{
    SerialCtrl.proccnt = 0;
    SerialCtrl.curunit = SerialCtrl.curunit->next;
    if(SerialCtrl.curunit == NULL) {//complete packet proc
        resetSerialProc();
    }
}

int8_t procSerialData(uint8_t databyte)
int8_t procSerialData(uint8_t databyte)
{
    //0:processing;1:done;-1:error and will reset by next call
    switch (SerialCtrl.curunit->type) {
        case SERIAL_PROC_TYPE_COMPARE:
            {
                if(SerialCtrl.proccnt < SerialCtrl.curunit->unitlen) {
                    if(*(((uint8_t *)(SerialCtrl.curunit->ptr) + SerialCtrl.proccnt)) == databyte) {
                        SerialCtrl.proccnt++;
                        if(SerialCtrl.proccnt == SerialCtrl.curunit->unitlen)
                            jumpSerialNext();
                    } else {
                        resetSerialProc();
                    }
                } else {
                    resetSerialProc();
                }
            }
            break;
        case SERIAL_PROC_TYPE_SAVE:
            {
                if(SerialCtrl.packlen) {
                    SerialCtrl.packlen--;
                    *SerialCtrl.dataptr = databyte;
                    SerialCtrl.dataptr++;
                } else {
                    jumpSerialNext();
                    SerialCtrl.dataptr = SerialProc.buf;
                }
            }
            break;
        case SERIAL_PROC_TYPE_CHECK:
            {
                if(SerialCtrl.proccnt < SerialCtrl.curunit->unitlen) {
                    *(((uint8_t *)(SerialCtrl.curunit->ptr) + SerialCtrl.proccnt)) = databyte;
                    SerialCtrl.proccnt++;
                } else {
                    uint16_t ret = SerialProc.checkfun(SerialProc.buf, SerialCtrl.packlen);
                    if(ret == *(((uint16_t *)(SerialCtrl.curunit->ptr)))) {
                        jumpSerialNext();
                    } else {
                        resetSerialProc();
                    }
                }
            }
            break;
        case SERIAL_PROC_TYPE_PACKLEN:
            {
                if(SerialCtrl.proccnt < SerialCtrl.curunit->unitlen) {
                    if(SerialProc.byteorder == SERIAL_BYTEORDER_SMALL) {
                        SerialCtrl.packlen += (databyte << (SerialCtrl.proccnt * 8));
                    } else {
                        SerialCtrl.packlen <<= 8;
                        SerialCtrl.packlen += databyte;
                    }
                } else {
                    jumpSerialNext();
                }
            }
            break;
        default :
            resetSerialProc();
            break;
    }
    if(SerialCtrl.curstate != SerialCtrl.curunit->type) {
        SerialCtrl.curstate = SerialCtrl.curunit->type;
    }
}